// -----------------------------------------------------------------------
//  <copyright file="SettingStore.cs" company="LiuliuSoft">
//      Copyright (c) 2022-2024 DaprPlus. All rights reserved.
//  </copyright>
//  <site>https://www.dapr.plus</site>
//  <last-editor>郭明锋</last-editor>
//  <last-date>2024-04-15 21:24</last-date>
// -----------------------------------------------------------------------

using System.Reflection;
using System.Text.Json;

using DaprPlus.Domain;
using DaprPlus.Domain.Entity;
using DaprPlus.Settings.Domain.Entities;
using DaprPlus.Settings.Domain.Dtos;
using DaprPlus.Settings.Domain.Specs;

using Microsoft.Extensions.Caching.Distributed;

using OSharp.Caching;
using DaprPlus.Mapping;


namespace DaprPlus.Settings.Domain.Services;

public class SettingStore(IServiceProvider provider) : ISettingStore
{
    private readonly IRepository<SettingDefinition> _repository = provider.GetRepository<SettingDefinition>();
    private readonly ILogger<SettingStore> _logger = provider.GetRequiredService<ILogger<SettingStore>>();
    private readonly IDistributedCache _cache = provider.GetRequiredService<IDistributedCache>();

    // 缓存键前缀
    private const string _SettingDefinitionCacheKey = "SettingDefinition:";
    private const string _SettingItemsCacheKey = "SettingItems:";
    private const string _SettingObjectCacheKey = "SettingObject:";

    /// <summary>
    /// 根据设置类型名称获取强类型设置对象
    /// 如果数据库中没有数据，则从强类型类创建默认值
    /// </summary>
    /// <typeparam name="TSetting">设置类型，必须实现ISetting接口</typeparam>
    /// <param name="cancellationToken">取消令牌</param>
    /// <returns>强类型设置对象</returns>
    public async Task<TSetting> GetSettingAsync<TSetting>(CancellationToken cancellationToken = default) where TSetting : class, ISetting, new()
    {
        var typeName = typeof(TSetting).FullName!;

        // 1. 创建强类型实例
        var setting = new TSetting();

        // 2. 从数据库获取设置定义
        var definitionDto = await GetSettingDefinitionOrNullAsync(typeName, cancellationToken);

        if (definitionDto != null)
        {
            // 3. 使用数据库中的值更新强类型实例
            UpdateSettingFromDefinition(setting, definitionDto);
        }

        return setting;
    }

    /// <summary>
    /// 根据设置类型名称获取设置定义（可能返回null）
    /// </summary>
    /// <param name="typeName">设置类型名称</param>
    /// <param name="cancellationToken">取消令牌</param>
    /// <returns>设置定义，如果不存在则返回null</returns>
    public async Task<SettingDefinitionOutDto?> GetSettingDefinitionOrNullAsync(string typeName, CancellationToken cancellationToken = default)
    {
        var cacheKey = _SettingDefinitionCacheKey + typeName;

        // 尝试从缓存获取
        var cachedDto = await _cache.GetAsync<SettingDefinitionOutDto>(cacheKey, cancellationToken);
        if (cachedDto != null)
        {
            return cachedDto;
        }

        // 从数据库获取
        var spec = new SettingDefinitionByTypeNameWithItemsSpec(typeName);
        var definition = await _repository.GetAsync(spec, cancellationToken);

        if (definition == null)
        {
            return null;
        }

        // 映射为OutDto
        var dto = definition.MapTo<SettingDefinitionOutDto>();

        // 缓存结果
        var seconds = (int)TimeSpan.FromDays(7).TotalSeconds;
        await _cache.SetAsync(cacheKey, dto, seconds, cancellationToken);

        return dto;
    }

    /// <summary>
    /// 根据设置类型名称获取设置定义，如果不存在则创建
    /// 如果数据库中没有数据，则从设置类型类创建默认值
    /// </summary>
    /// <param name="typeName">设置类型名称</param>
    /// <param name="cancellationToken">取消令牌</param>
    /// <returns>设置定义，如果不存在则从设置类型创建</returns>
    public async Task<SettingDefinitionOutDto> GetOrCreateSettingDefinitionAsync(string typeName, CancellationToken cancellationToken = default)
    {
        // 先尝试获取现有的
        var existing = await GetSettingDefinitionOrNullAsync(typeName, cancellationToken);
        if (existing != null)
        {
            return existing;
        }

        // 从类型创建默认定义
        var definition = CreateDefaultSettingDefinition(typeName);

        // 保存到数据库
        await _repository.InsertAsync(definition, cancellationToken);

        // 映射为OutDto
        var dto = definition.MapTo<SettingDefinitionOutDto>();

        // 缓存结果
        var cacheKey = _SettingDefinitionCacheKey + typeName;
        var seconds = (int)TimeSpan.FromMinutes(30).TotalSeconds;
        await _cache.SetAsync(cacheKey, dto, seconds, cancellationToken);

        return dto;
    }

    /// <summary>
    /// 保存设置定义到数据库
    /// </summary>
    /// <param name="settingDto">设置定义对象</param>
    /// <param name="cancellationToken">取消令牌</param>
    /// <returns>保存结果</returns>
    public async Task<ApiResult> SaveSettingAsync(SettingDefinitionInDto settingDto, CancellationToken cancellationToken = default)
    {
        var spec = new SettingDefinitionByTypeNameWithItemsSpec(settingDto.TypeName);
        var existing = await _repository.GetAsync(spec, cancellationToken);

        if (existing == null)
        {
            // 创建新的
            var newDefinition = settingDto.MapTo<SettingDefinition>();
            await _repository.InsertAsync(newDefinition, cancellationToken);
        }
        else
        {
            // 更新现有的
            settingDto.MapTo(existing);

            // 更新设置项
            foreach (var itemDto in settingDto.Items)
            {
                var item = ConvertSettingItemDto(itemDto);
                existing.AddOrUpdateItem(item);
            }

            await _repository.UpdateAsync(existing, cancellationToken);
        }

        // 清除缓存
        await ClearCacheAsync(settingDto.TypeName, cancellationToken);

        return ApiResult.Success($"[{settingDto.Name}] 保存成功。");
    }

    /// <summary>
    /// 保存单个设置项
    /// </summary>
    /// <param name="typeName">设置类型名称</param>
    /// <param name="settingItem">设置项</param>
    /// <param name="cancellationToken">取消令牌</param>
    /// <returns>保存结果</returns>
    public async Task<ApiResult> SaveSettingItemAsync(string typeName, SettingItemInDto settingItem, CancellationToken cancellationToken = default)
    {
        var spec = new SettingDefinitionByTypeNameWithItemsSpec(typeName);
        var definition = await _repository.GetAsync(spec, cancellationToken);

        if (definition == null)
        {
            return ApiResult.Error($"设置定义不存在: {typeName}");
        }

        // 创建设置项
        var item = ConvertSettingItemDto(settingItem);
        definition.AddOrUpdateItem(item);

        await _repository.UpdateAsync(definition, cancellationToken);

        // 清除缓存
        await ClearCacheAsync(typeName, cancellationToken);

        return ApiResult.Success();
    }

    /// <summary>
    /// 根据设置类型名称删除所有相关设置
    /// </summary>
    /// <param name="typeName">设置类型名称</param>
    /// <param name="cancellationToken">取消令牌</param>
    /// <returns>删除结果</returns>
    public async Task<ApiResult> DeleteSettingsAsync(string typeName, CancellationToken cancellationToken = default)
    {
        var spec = new SettingDefinitionByTypeNameWithItemsSpec(typeName);
        var definition = await _repository.GetAsync(spec, cancellationToken);

        if (definition == null)
        {
            return ApiResult.Error($"设置定义不存在: {typeName}");
        }

        await _repository.DeleteAsync(definition, cancellationToken);

        // 清除缓存
        await ClearCacheAsync(typeName, cancellationToken);

        return ApiResult.Success();
    }

    /// <summary>
    /// 根据设置类型名称和属性名删除单个设置项
    /// </summary>
    /// <param name="typeName">设置类型名称</param>
    /// <param name="propertyName">属性名</param>
    /// <param name="cancellationToken">取消令牌</param>
    /// <returns>删除结果</returns>
    public async Task<ApiResult> DeleteSettingItemAsync(string typeName, string propertyName, CancellationToken cancellationToken = default)
    {
        var spec = new SettingDefinitionByTypeNameWithItemsSpec(typeName);
        var definition = await _repository.GetAsync(spec, cancellationToken);

        if (definition == null)
        {
            return ApiResult.Error($"设置定义不存在: {typeName}");
        }

        var removed = definition.RemoveItem(propertyName);
        if (!removed)
        {
            return ApiResult.Error($"设置项不存在: {propertyName}");
        }

        await _repository.UpdateAsync(definition, cancellationToken);

        // 清除缓存
        await ClearCacheAsync(typeName, cancellationToken);

        return ApiResult.Success();
    }

    /// <summary>
    /// 重置指定类型的设置项到类型设定的默认值
    /// </summary>
    /// <param name="typeName">设置类型名称</param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public async Task<ApiResult<SettingDefinitionOutDto>> ResetSettingAsync(string typeName, CancellationToken cancellationToken = default)
    {
        // 1. 从类型创建默认的设置定义
        var defaultDefinition = CreateDefaultSettingDefinition(typeName);

        // 2. 查找现有的设置定义
        var spec = new SettingDefinitionByTypeNameWithItemsSpec(typeName);
        var existing = await _repository.GetAsync(spec, cancellationToken);

        if (existing == null)
        {
            // 如果不存在，直接插入新的默认定义
            await _repository.InsertAsync(defaultDefinition, cancellationToken);
        }
        else
        {
            // 如果存在，更新为默认值
            // 保留ID和基本信息，但重置所有设置项为默认值
            existing.Name = defaultDefinition.Name;
            existing.Remark = defaultDefinition.Remark;

            // 添加默认设置项
            foreach (var defaultItem in defaultDefinition.Items)
            {
                existing.AddOrUpdateItem(defaultItem);
            }

            await _repository.UpdateAsync(existing, cancellationToken);
        }

        // 3. 映射为OutDto
        var resultDto = existing != null
            ? existing.MapTo<SettingDefinitionOutDto>()
            : defaultDefinition.MapTo<SettingDefinitionOutDto>();

        // 4. 清除缓存
        await ClearCacheAsync(typeName, cancellationToken);

        // 5. 缓存新的结果
        var cacheKey = _SettingDefinitionCacheKey + typeName;
        var seconds = (int)TimeSpan.FromMinutes(30).TotalSeconds;
        await _cache.SetAsync(cacheKey, resultDto, seconds, cancellationToken);

        return ApiResult<SettingDefinitionOutDto>.Success("设置已重置为默认值", resultDto);
    }

    #region 私有方法

    /// <summary>
    /// 从类型创建默认的设置定义
    /// </summary>
    /// <param name="typeName">类型名称</param>
    /// <returns>设置定义</returns>
    private SettingDefinition CreateDefaultSettingDefinition(string typeName)
    {
        _logger.LogDebug("从类型创建设置定义: {TypeName}", typeName);

        // 获取设置类型
        var settingType = Type.GetType(typeName);
        if (settingType == null)
        {
            throw new InvalidOperationException($"无法找到类型: {typeName}");
        }

        // 验证类型是否实现ISetting接口
        if (!typeof(ISetting).IsAssignableFrom(settingType))
        {
            throw new ArgumentException($"类型 {settingType.Name} 必须实现 ISetting 接口");
        }

        // 创建设置定义
        var definition = new SettingDefinition
        {
            Name = settingType.GetDescription(),
            TypeName = typeName,
            Items = []
        };
        definition.SetId();

        // 创建设置类型实例以获取默认值
        var settingInstance = Activator.CreateInstance(settingType);
        if (settingInstance != null)
        {
            // 从设置类型实例提取默认设置项
            var defaultItems = ExtractSettingItemsFromObjectAsync(settingInstance, definition.Id);
            definition.AddOrUpdateItems(defaultItems);
        }

        return definition;
    }

    private SettingItem[] ExtractSettingItemsFromObjectAsync(object setting, long definitionId)
    {
        var settingType = setting.GetType();
        var properties = settingType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
            .Where(p => p.CanRead)
            .ToList();

        var settingItems = new List<SettingItem>();

        foreach (var property in properties)
        {
            try
            {
                var value = property.GetValue(setting);
                if (value is not SettingItem item)
                {
                    continue;
                }

                item.SettingDefinitionId = definitionId;
                item.Display = property.GetDescription();
                settingItems.Add(item);
            }
            catch (Exception ex)
            {
                _logger.LogWarning(ex, "提取属性值失败: {PropertyName}", property.Name);
            }
        }

        return settingItems.ToArray();
    }

    /// <summary>
    /// 获取类型的默认值
    /// </summary>
    /// <param name="type">类型</param>
    /// <returns>默认值</returns>
    private static object? GetDefaultValue(Type type)
    {
        if (type.IsValueType)
        {
            return Activator.CreateInstance(type);
        }
        return null;
    }

    /// <summary>
    /// 使用设置定义更新强类型设置对象
    /// </summary>
    /// <param name="setting">强类型设置对象</param>
    /// <param name="definition">设置定义</param>
    private static void UpdateSettingFromDefinition(object setting, SettingDefinitionOutDto definition)
    {
        var type = setting.GetType();

        foreach (var item in definition.Items)
        {
            var property = type.GetProperty(item.Key);
            if (property == null || !property.CanWrite)
            {
                continue;
            }

            try
            {
                var value = ConvertValue(item.Value, property.PropertyType);
                property.SetValue(setting, value);
            }
            catch (Exception)
            {
                // 忽略转换失败的情况，使用默认值
            }
        }
    }

    /// <summary>
    /// 转换值到指定类型
    /// </summary>
    /// <param name="value">原始值</param>
    /// <param name="targetType">目标类型</param>
    /// <returns>转换后的值</returns>
    private static object? ConvertValue(object? value, Type targetType)
    {
        if (value == null)
        {
            return targetType.IsValueType ? Activator.CreateInstance(targetType) : null;
        }

        if (targetType.IsInstanceOfType(value))
        {
            return value;
        }

        try
        {
            return Convert.ChangeType(value, targetType);
        }
        catch
        {
            // 尝试JSON反序列化
            if (value is string jsonString)
            {
                try
                {
                    return JsonSerializer.Deserialize(jsonString, targetType);
                }
                catch
                {
                    // 忽略JSON反序列化失败
                }
            }

            return targetType.IsValueType ? Activator.CreateInstance(targetType) : null;
        }
    }

    /// <summary>
    /// 转换设置项DTO，处理JsonElement类型
    /// </summary>
    /// <param name="itemDto">设置项DTO</param>
    /// <returns>设置项实体</returns>
    private static SettingItem ConvertSettingItemDto(SettingItemInDto itemDto)
    {
        var item = new SettingItem
        {
            Id = itemDto.Id,
            Key = itemDto.Key,
            Display = itemDto.Display,
            Remark = itemDto.Remark,
            Order = itemDto.Order,
            IsLocked = itemDto.IsLocked,
            SettingDefinitionId = itemDto.SettingDefinitionId
        };

        // 处理值，确保类型信息正确
        if (itemDto.Value != null)
        {
            // 如果值是JsonElement，需要特殊处理
            if (itemDto.Value is JsonElement jsonElement)
            {
                // 从JsonElement中提取实际值并设置正确的类型
                var actualValue = jsonElement.ConvertToObject();
                item.Value = actualValue;
            }
            else
            {
                // 直接设置值
                item.Value = itemDto.Value;
            }
        }

        return item;
    }

    /// <summary>
    /// 清除指定类型名称的缓存
    /// </summary>
    /// <param name="typeName">类型名称</param>
    /// <param name="cancellationToken">取消令牌</param>
    private async Task ClearCacheAsync(string typeName, CancellationToken cancellationToken = default)
    {
        var cacheKeys = new[]
        {
            _SettingDefinitionCacheKey + typeName,
            _SettingItemsCacheKey + typeName,
            _SettingObjectCacheKey + typeName
        };

        foreach (var key in cacheKeys)
        {
            await _cache.RemoveAsync(key, cancellationToken);
        }
    }

    #endregion
}

